const unpackMode = 1;
export class ConfigZip {
    private static _zips: ZipUnpacker[];
    private static progressCallBack: Function;
    private static completeCallBack: Function;

    private static TotalProgressCallBack: Function;
    private static TotalCompleteCallBack: Function;

    public static time: number;

    public static unpackZip(zipPaths: { zipPath: string, encry: boolean }[], progressCallBack: Function = null, completeCallBack: Function = null) {
        ConfigZip.dispose();
        ConfigZip.TotalProgressCallBack = progressCallBack;
        ConfigZip.TotalCompleteCallBack = completeCallBack;

        ConfigZip.progressCallBack = ConfigZip.onProgressCallBack;
        ConfigZip.completeCallBack = ConfigZip.onCompleteCallBack;

        ConfigZip._zips = [];
        ConfigZip.time = Date.now() / 1000;
        zipPaths.forEach(element => {
            var _zip = new ZipUnpacker();
            ConfigZip._zips.push(_zip);
            //微信需要.bin结尾下载二进制
            if (cc.sys.platform === cc.sys.WECHAT_GAME) {
                element.zipPath = element.zipPath.replace(".zip", ".bin");
            }
            _zip.unpackZip(element.zipPath, ConfigZip.progressCallBack, ConfigZip.completeCallBack, element.encry);
        });
    }

    private static onProgressCallBack() {
        if (ConfigZip._zips && ConfigZip._zips.length > 0) {
            var _progress: number = 0;
            ConfigZip._zips.forEach(element => {
                if (element)
                    _progress += element.progress;
            });
            this.TotalProgressCallBack && this.TotalProgressCallBack(_progress / ConfigZip._zips.length);
        }
    }

    private static onCompleteCallBack() {
        if (ConfigZip._zips) {
            var isComplete: boolean = true;
            ConfigZip._zips.forEach(element => {
                if (element && element.isComplete == false)
                    isComplete = false;
            });
            if (isComplete) {
                this.TotalCompleteCallBack && this.TotalCompleteCallBack();
                console.log("配置表解压：", Date.now() / 1000 - this.time);
            }
        }
    }

    public static dispose() {
        if (ConfigZip._zips) {
            ConfigZip._zips.forEach(zip => {
                zip.dispose();
            });
        }
        ConfigZip._zips = null;
        if (ConfigZip.progressCallBack)
            ConfigZip.progressCallBack = null;
        if (ConfigZip.completeCallBack)
            ConfigZip.completeCallBack = null;
        ConfigZip.progressCallBack = null;
        ConfigZip.completeCallBack = null;
    }
}


class ZipUnpacker {

    private totalSize: number = 0;
    private currentSize: number = 0;
    private progressCallBack: Function;
    private completeCallBack: Function;
    private zipPath: string;
    public isComplete: boolean = false;
    public encry: boolean = true;
    private time: number;

    /**
     * 解压
     * @param zipPath 路径
     * @param progressCallBack 进度
     * @param completeCallBack 完成回调 
     * @param encry 是否是加密文件
     * @param forceJSZip  担心微信小程序解压失败，失败的时候可以强制使用jsZip来解压一次
     */
    async unpackZip(zipPath: string, progressCallBack: Function = null, completeCallBack: Function = null, encry: boolean = true, forceJSZip: boolean = false) {
        this.isComplete = false;
        this.time = Date.now() / 1000;
        this.totalSize = 1000000000;
        this.currentSize = 0;
        this.zipPath = zipPath;
        this.encry = encry;

        this.progressCallBack = progressCallBack;
        this.completeCallBack = completeCallBack;
        this.progressCallBack && this.progressCallBack(this.progress);

        console.warn("开始JsZip解压");

        var data: any = await this.loadZip(zipPath);
        if (!data) return;
        var bin = new Uint8Array(data);
        if (encry) {
            let len = bin.length;
            let passwordIndex = 0;
            let password = new Uint8Array([32, 45, 78, 11, 30, 87, 35, 2, 12]);
            for (let index = 0; index < len; index++) {
                bin[index] = bin[index] ^ password[passwordIndex];
                passwordIndex++;
                if (passwordIndex >= password.length)
                    passwordIndex = 0;
            }
        }

        let jszip = new JSZip();
        await jszip.loadAsync(bin, {});
        let plist = [];
        var THIS: ZipUnpacker = this;
        this.totalSize = 0;
        jszip.forEach((relativePath: string, file: JSZipObject) => {
            let fileExName = file.name.split('.').pop().toLowerCase();
            if (file && file["_data"] && file["_data"]["uncompressedSize"] > 0)
                THIS.totalSize += file["_data"]["uncompressedSize"];
            switch (fileExName) {
                case 'json':
                    plist.push(file.async('string').then((v) => {

                        let fileName = THIS.getFileName(file.name);
                        if (unpackMode == 1) {
                            //用到的时候再解压，省点内存，（这种模式在android包下会解压有问题，先只在微信小程序环境使用）
                            // Laya.Loader.cacheRes(JsonManager.getLocalPathByName(fileName), v);
                            console.log(`fileName:${fileName}, v:${v}`);
                        }
                        else {
                            //一次性解压
                            try {
                                let obj = JSON.parse(v);
                                console.log(`fileName:${fileName}, v:${obj}`);
                            } catch (err) {
                                console.error(fileName + "表格解压报错", relativePath, err);
                            };
                        }

                        if (file && file["_data"] && file["_data"]["uncompressedSize"] > 0)
                            THIS.currentSize += file["_data"]["uncompressedSize"];
                        THIS.progressCallBack && THIS.progressCallBack(THIS.progress);
                    }));
                    break;
                default:
                    plist.push(file.async('arraybuffer').then((v) => {
                        console.error("can't unzip:", file.name);
                        if (file && file["_data"] && file["_data"]["uncompressedSize"] > 0)
                            THIS.currentSize += file["_data"]["uncompressedSize"];
                        THIS.progressCallBack && THIS.progressCallBack(THIS.progress);
                    }));
                    break;
            }
        });

        await Promise.all(plist);
        this.isComplete = true;
        this.progressCallBack && this.progressCallBack(1);
        this.completeCallBack && this.completeCallBack();
        this.progressCallBack = null;
        this.completeCallBack = null;

        console.log("解压完成时间:", Date.now() / 1000 - this.time);
        // this.event(Laya.Event.COMPLETE);
    }

    private loadZip(url: string) {
        let path: string = cc.url.raw(url);
        return new Promise((resolve, reject) => {
            cc.loader.load({ url: path, type: "binary" }, (err, res) => {
                if (err) return reject(err);
                console.log(res);
                resolve(res);
            });
        });
    }

    public dispose() {
        cc.loader.release(this.zipPath);
        this.progressCallBack = null;
        this.completeCallBack = null;
        this.isComplete = false;
    }

    public get progress() {
        return this.currentSize / this.totalSize;
    }

    getFileName(path: string): string {
        let fileName = path.split('.').shift();
        let n = fileName.lastIndexOf("/");
        if (n != -1) {
            fileName = fileName.substr(n + 1);
        };
        return fileName;
    }
}